using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Text;
using System.Xml;
using System.Xml.XPath;
using System.Xml.Xsl;
using Microsoft.Build.Framework;
using Microsoft.Build.Utilities;
using Microsoft.Win32;

namespace CriticalSystems.Instrumentation
{
    /// <summary>
    /// MSBuild task that creates an event log resource file.
    /// </summary>
    public class SystemInformationTask : Task
    {
        private string systemInformation;
        private string projectPath;
        private string win32Resource;
        private string xmlResource;
        private string momPack = string.Empty;
        private bool generateMomPack;

        [Required]
        public string SystemInformation
        {
            get { return this.systemInformation; }
            set { this.systemInformation = value; }
        }

        [Required]
        public string ProjectPath
        {
            get { return this.projectPath; }
            set { this.projectPath = value; }
        }

        public bool GenerateMomPack
        {
            get { return this.generateMomPack; }
            set { this.generateMomPack = value; }
        }

        [Output]
        public string Win32Resource
        {
            get { return this.win32Resource; }
        }

        [Output]
        public string XmlResource
        {
            get { return this.xmlResource; }
        }

        [Output]
        public string MomPack
        {
            get { return this.momPack; }
        }

        private static string GetToolsPath()
        {
            using (RegistryKey regKey = Registry.LocalMachine.OpenSubKey(@"SOFTWARE\Microsoft\VisualStudio\8.0", false))
            {
                string vsPath = regKey.GetValue("InstallDir").ToString();
                return Path.Combine(vsPath, @"..\Tools\bin");
            }
        }

        private static string GetMcCommandLine()
        {
            return string.Format(CultureInfo.InvariantCulture, @"{0}\mc.exe", GetToolsPath());
        }

        private static string GetRcCommandLine()
        {
            return string.Format(CultureInfo.InvariantCulture, @"{0}\rc.exe", GetToolsPath());
        }

        private static string GetResourcePath()
        {
            return "SystemInformation.rc";
        }

        private static string GetMessagesPath()
        {
            return "SystemInformation.mc";
        }

        private static string GetWin32ResourcePath()
        {
            return "SystemInformation.res";
        }

        private static string GetXmlResourcePath()
        {
            return "SystemInformation.xml";
        }

        /// <summary>
        /// When overridden in a derived class, executes the task.
        /// </summary>
        /// <returns>
        /// true if the task successfully executed; otherwise, false.
        /// </returns>
        public override bool Execute()
        {
            bool success;
            try
            {
                Log.LogMessage("Starting");
                IXPathNavigable xml = new XPathDocument(new StringReader(this.systemInformation));
                XPathNavigator navigator = xml.CreateNavigator();
                navigator.MoveToFirstChild();
                string system = navigator.GetAttribute("name", string.Empty);
                this.win32Resource = GetWin32ResourcePath();
                Log.LogMessage("Loaded system information XML for system {0}", system);
                this.xmlResource = GetXmlResourcePath();
                using (StreamWriter writer = new StreamWriter(GetXmlResourcePath(), false))
                {
                    writer.Write(xml.CreateNavigator().OuterXml);
                }
                Log.LogMessage("Written xml file");
                XslCompiledTransform transform = new XslCompiledTransform();
                IXPathNavigable xslt =
                    new XPathDocument(
                        ResourceUtilities.GetEmbeddedResource("CriticalSystems.Instrumentation.CreateSystemInformationMessages.xslt"));
                transform.Load(xslt);
                Log.LogMessage("Loaded message transform");
                using (StreamWriter output = new StreamWriter(GetMessagesPath(), false, Encoding.ASCII))
                {
                    transform.Transform(xml, null, output);
                }
                Log.LogMessage("Written message file");
                if (!File.Exists(GetMcCommandLine()))
                {
                    Log.LogError("Can't find mc tool '{0}'", GetMcCommandLine());
                    success = false;
                }
                else
                {
                    success = RunProcess(GetMcCommandLine(), GetMessagesPath());
                    if (success)
                    {
                        Log.LogMessage("Ran mc process");
                        success = RunProcess(GetRcCommandLine(),
                                             string.Format(CultureInfo.InvariantCulture, "/r {0}", GetResourcePath()));
                        if (success)
                        {
                            Log.LogMessage("Ran rc process");
                        }
                    }
                }
                if (success && this.generateMomPack)
                {
                    this.momPack = GetMomPackPath(system);
                    Log.LogMessage("Generating MOM pack {0}", momPack);
                    transform = new XslCompiledTransform();
                    xslt =
                        new XPathDocument(
                            ResourceUtilities.GetEmbeddedResource("CriticalSystems.Instrumentation.CreateMomPack.xslt"));
                    transform.Load(xslt);
                    Log.LogMessage("Loaded MOM transform");
                    using (StreamWriter output = new StreamWriter(this.momPack, false, Encoding.UTF8))
                    {
                        transform.Transform(xml, null, output);
                    }
                    Log.LogMessage("Written MOM pack file {0}", momPack);
                }
            }
            catch (XmlException xex)
            {
                Log.LogErrorFromException(xex);
                success = false;
            }
            return success;
        }

        private static string GetMomPackPath(string system)
        {
            return string.Format(CultureInfo.InvariantCulture, "{0}.Pack.xml", system);
        }

        private bool RunProcess(string fileName, string arguments)
        {
            bool success = true;
            Process process = new Process();
            process.StartInfo.FileName = fileName;
            process.StartInfo.Arguments = arguments;
            process.StartInfo.CreateNoWindow = true;
            process.StartInfo.UseShellExecute = false;
            process.StartInfo.RedirectStandardError = true;
            process.Start();

            StreamReader errorReader = process.StandardError;
            process.WaitForExit();
            int exitCode = process.ExitCode;
            if (exitCode != 0)
            {
                Log.LogMessagesFromStream(errorReader, MessageImportance.High);
                success = false;
            }
            return success;
        }
    }
}